/*
 * Copyright (c) 2022 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "file_operator.h"
#include "adaptor_log.h"
#include "defines.h"
#include "tee_defines.h"
#include "tee_object_api.h"
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <tee_core_api.h>
#include <tee_ext_api.h>
#include <tee_log.h>
#include <tee_mem_mgmt_api.h>
#include <tee_trusted_storage_api.h>
#include <unistd.h>

bool IsFileExist(const char *fileName) {
  TEE_Result result;
  TEE_ObjectHandle handle = TEE_HANDLE_NULL;
  const int32_t flags = TEE_DATA_FLAG_ACCESS_READ | TEE_DATA_FLAG_ACCESS_WRITE;

  result = TEE_OpenPersistentObject(TEE_OBJECT_STORAGE_PRIVATE, fileName,
                                    strlen(fileName), flags, &handle);
  if (result == TEE_SUCCESS) {
    TEE_CloseObject(handle);
    return true;
  } else if (result == TEE_ERROR_ITEM_NOT_FOUND) {
    return false;
  } else {
    LOG_ERROR("Some error other than TEE_ERROR_ITEM_NOT_FOUND happens. this "
              "should never happen!");
    return false;
  }
}

static int32_t ReadFile(const char *fileName, uint8_t *buf, uint32_t len) {
  TEE_Result result;
  TEE_ObjectHandle fileHandle = TEE_HANDLE_NULL;
  uint32_t count = 0;

  if ((fileName == NULL) || (buf == NULL) || (len == 0)) {
    LOG_ERROR("get bad params");
    return RESULT_BAD_PARAM;
  }

  result = TEE_OpenPersistentObject(TEE_OBJECT_STORAGE_PRIVATE, fileName,
                                    strlen(fileName), TEE_DATA_FLAG_ACCESS_READ,
                                    &fileHandle);
  if (result != TEE_SUCCESS) {
    LOG_ERROR("open file fail");
    return RESULT_BAD_PARAM;
  }

  result = TEE_ReadObjectData(fileHandle, buf, len, &count);

  if (result != TEE_SUCCESS || count != len) {
    TEE_CloseObject(fileHandle);
    return RESULT_BAD_READ;
  }

  TEE_CloseObject(fileHandle);
  return RESULT_SUCCESS;
}

static int32_t DeleteFile(const char *fileName) {
  TEE_Result result;
  TEE_ObjectHandle fileHandle = TEE_HANDLE_NULL;

  if (fileName == NULL) {
    LOG_ERROR("get bad params");
    return RESULT_BAD_PARAM;
  }

  result = TEE_OpenPersistentObject(
      TEE_OBJECT_STORAGE_PRIVATE, fileName, strlen(fileName),
      TEE_DATA_FLAG_ACCESS_READ | TEE_DATA_FLAG_ACCESS_WRITE_META, &fileHandle);

  if (result == TEE_ERROR_ITEM_NOT_FOUND) {
    return RESULT_SUCCESS;
  }

  if (result != TEE_SUCCESS) {
    LOG_ERROR("open file fail");
    return RESULT_GENERAL_ERROR;
  }

  TEE_CloseAndDeletePersistentObject1(fileHandle);
  return RESULT_SUCCESS;
}

static int32_t WriteFile(const char *fileName, const uint8_t *buf,
                         uint32_t len) {
  TEE_Result result;
  TEE_ObjectHandle fileHandle = TEE_HANDLE_NULL;

  if ((fileName == NULL) || (buf == NULL) || (len == 0)) {
    LOG_ERROR("get bad params");
    return RESULT_BAD_PARAM;
  }

  if (IsFileExist(fileName)) {
    if (DeleteFile(fileName) != RESULT_SUCCESS) {
      LOG_ERROR("delete file fail");
      return RESULT_GENERAL_ERROR;
    }
  }
  result = TEE_CreatePersistentObject(
      TEE_OBJECT_STORAGE_PRIVATE, fileName, strlen(fileName),
      TEE_DATA_FLAG_ACCESS_WRITE | TEE_DATA_FLAG_CREATE |
          TEE_DATA_FLAG_OVERWRITE,
      TEE_HANDLE_NULL, buf, len, &fileHandle);

  if (result != TEE_SUCCESS) {
    LOG_ERROR("create file fail");
    return RESULT_BAD_PARAM;
  }


  TEE_CloseObject(fileHandle);
  return RESULT_SUCCESS;
}

static int32_t GetFileLen(const char *fileName, uint32_t *len) {
  TEE_Result result;
  TEE_ObjectHandle fileHandle = TEE_HANDLE_NULL;
  TEE_ObjectInfo info = {0};

  if ((fileName == NULL) || (len == NULL)) {
    LOG_ERROR("get bad params");
    return RESULT_BAD_PARAM;
  }

  result = TEE_OpenPersistentObject(TEE_OBJECT_STORAGE_PRIVATE, fileName,
                                    strlen(fileName), TEE_DATA_FLAG_ACCESS_READ,
                                    &fileHandle);
  if (result != TEE_SUCCESS) {
    LOG_ERROR("fopen file fail");
    return RESULT_BAD_PARAM;
  }

  result = TEE_GetObjectInfo1(fileHandle, &info);
  if (result != TEE_SUCCESS) {
    LOG_ERROR("get file info fail");
    TEE_CloseObject(fileHandle);
    return RESULT_GENERAL_ERROR;
  }

  *len = info.dataSize;
  TEE_CloseObject(fileHandle);

  return RESULT_SUCCESS;
}

FileOperator *GetDefaultFileOperator(void) {
  static FileOperator fileOperator = {
      .isFileExist = IsFileExist,
      .getFileLen = GetFileLen,
      .readFile = ReadFile,
      .writeFile = WriteFile,
      .deleteFile = DeleteFile,
  };
  return &fileOperator;
}
